home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-01-15 | 22.2 KB | 766 lines | [TEXT/R*ch] |
- // Solution to SlidingTiles MacTech Programmer's Challenge
- // Written by Jorg Brown & Eric Gundrum
- // Thanks also to Brad Kollmyer
-
- #include "Solve.h"
-
- static MoveProc gMakeMove;
- static long *gTiles, gNumCols;
-
- static long *gPieceRow, *gPieceCol;
-
- static long *gBlankSquare; // initialized by SolveTiles, updated by BlankXXX()
-
- #define gBlankRow pieceRow[0]
- #define gBlankCol pieceCol[0]
-
- static void BlankUp() { // move the BLANK SQUARE up
- long tile;
- long *oldBlankSquare;
- long *newBlankSquare;
- long *pieceRow = gPieceRow;
- long *pieceCol = gPieceCol;
-
- oldBlankSquare = gBlankSquare;
- gBlankSquare = newBlankSquare = oldBlankSquare - gNumCols;
- tile = *newBlankSquare;
- pieceRow[tile] = gBlankRow--;
- gMakeMove(gBlankRow, gBlankCol);
- *oldBlankSquare = tile;
- *newBlankSquare = 0;
- }
-
- static void BlankDown() { // move the BLANK SQUARE down
- long tile;
- long *oldBlankSquare;
- long *newBlankSquare;
- long *pieceRow = gPieceRow;
- long *pieceCol = gPieceCol;
-
- oldBlankSquare = gBlankSquare;
- gBlankSquare = newBlankSquare = oldBlankSquare + gNumCols;
- tile = *newBlankSquare;
- pieceRow[tile] = gBlankRow++;
- gMakeMove(gBlankRow, gBlankCol);
- *oldBlankSquare = tile;
- *newBlankSquare = 0;
- }
-
- static void MoveBlankToRow(long destRow) {
- long tile;
- long *oldBlankSquare;
- long *newBlankSquare;
- long *pieceRow = gPieceRow;
- long *pieceCol = gPieceCol;
- long blankRow = gBlankRow;
-
- long rowInc;
- long BlankSquareInc;
-
- if (blankRow < destRow) { // move DOWN
- rowInc = 1;
- BlankSquareInc = gNumCols;
- } else if (blankRow > destRow) { // move UP
- rowInc = -1;
- BlankSquareInc = -gNumCols;
- } else return;
-
- oldBlankSquare = gBlankSquare;
- do {
- newBlankSquare = oldBlankSquare + BlankSquareInc;
- tile = *newBlankSquare;
- pieceRow[tile] = blankRow;
- blankRow += rowInc;
- gMakeMove(blankRow, gBlankCol);
- *oldBlankSquare = tile;
- *newBlankSquare = 0;
- oldBlankSquare = newBlankSquare;
- } while (blankRow != destRow);
- gBlankSquare = newBlankSquare;
- gBlankRow = blankRow;
- }
-
- static void BlankLeft() { // move the BLANK SQUARE left
- long tile;
- long *oldBlankSquare;
- long *newBlankSquare;
- long *pieceRow = gPieceRow;
- long *pieceCol = gPieceCol;
-
- oldBlankSquare = gBlankSquare;
- gBlankSquare = newBlankSquare = oldBlankSquare - 1;
- tile = *newBlankSquare;
- pieceCol[tile] = gBlankCol--;
- gMakeMove(gBlankRow, gBlankCol);
- *oldBlankSquare = tile;
- *newBlankSquare = 0;
- }
-
- static void BlankRight() { // move the BLANK SQUARE right
- long tile;
- long *oldBlankSquare;
- long *newBlankSquare;
- long *pieceRow = gPieceRow;
- long *pieceCol = gPieceCol;
-
- oldBlankSquare = gBlankSquare;
- gBlankSquare = newBlankSquare = oldBlankSquare + 1;
- tile = *newBlankSquare;
- pieceCol[tile] = gBlankCol++;
- gMakeMove(gBlankRow, gBlankCol);
- *oldBlankSquare = tile;
- *newBlankSquare = 0;
- }
-
- static void MoveBlankToCol(long destCol) {
- long tile;
- long *oldBlankSquare;
- long *newBlankSquare;
- long *pieceRow = gPieceRow;
- long *pieceCol = gPieceCol;
- long blankCol = gBlankCol;
-
- long inc;
-
- if (blankCol < destCol) { // move RIGHT
- inc = 1;
- } else if (blankCol > destCol) { // move LEFT
- inc = -1;
- } else return;
-
- oldBlankSquare = gBlankSquare;
- do {
- newBlankSquare = oldBlankSquare + inc;
- tile = *newBlankSquare;
- pieceCol[tile] = blankCol;
- blankCol += inc;
- gMakeMove(gBlankRow, blankCol);
- *oldBlankSquare = tile;
- *newBlankSquare = 0;
- oldBlankSquare = newBlankSquare;
- } while (blankCol != destCol);
- gBlankSquare = newBlankSquare;
- gBlankCol = blankCol;
- }
-
- static void MoveAPiece(long piece, long destRow, long destCol, long
- nextPiece) {
- long sourceRow;
- long sourceCol;
- long *pieceRow = gPieceRow;
- long *pieceCol = gPieceCol;
- long nextRow = gNumCols;
-
- sourceRow = pieceRow[piece];
- sourceCol = pieceCol[piece];
-
- if (sourceRow >= destRow) { // in this case, we have to move the tile up (or directly right) to get to its destination
- if (sourceRow == destRow) {
- if (sourceCol == destCol) return;
-
- // simplify: move the blank so that it is not to the right of the target
- if (gBlankCol > destCol) {
- MoveBlankToCol(destCol);
- }
-
- // move the blank to the left of the source, possibly moving the source
- // to the right at the same time.
- if (gBlankRow != destRow) {
- if (sourceCol == destCol - 1 && gBlankRow > destRow) {
- MoveBlankToCol(sourceCol - 1);
- MoveBlankToRow(sourceRow - 1);
- BlankRight();
- BlankRight();
- BlankDown();
- BlankLeft();
- return; // all done
- } else {
- MoveBlankToCol(sourceCol + 1);
- MoveBlankToRow(sourceRow);
- BlankLeft();
- sourceCol++;
- }
- } else {
- if (gBlankCol < sourceCol) {
- MoveBlankToCol(sourceCol - 1);
- } else {
- MoveBlankToCol(sourceCol);
- sourceCol++;
- }
- }
-
- WereOnTheSameRowNow:
- // at this point, the blank is to the left of the source,
- // and the puzzle might very well be done already.
- while (sourceCol != destCol) {
- if (nextPiece == piece - 1) { // into a row?
- if (gBlankCol != 0 && pieceCol[nextPiece]-pieceRow[nextPiece] <= gBlankCol-gBlankRow) {
- MoveAPiece(nextPiece, gBlankRow, gBlankCol, nextPiece - 1);
- }
- if (gBlankRow == destRow) while ((gBlankSquare[-1] == gBlankSquare[1] - 1) && gBlankCol != 0) {
- BlankLeft();
- }
- }
- if (gBlankRow == destRow) BlankUp();
- do {
- BlankRight();
- } while (gBlankCol <= sourceCol);
- BlankDown();
- BlankLeft();
- sourceCol++;
- }
- return;
- }
- // simplify: move the blank so that it is to the left of the target
- if (gBlankCol >= destCol) {
- MoveBlankToCol(destCol - 1);
- }
- // simplify: move the blank so that it is not above the target.
- if (gBlankRow < destRow) MoveBlankToRow(destRow);
-
- again1: // simplify: if the blank is below the source, move it so it's not.
- sourceRow = pieceRow[piece];
- sourceCol = pieceCol[piece];
- if (gBlankRow >= sourceRow) {
- // if the blank is in the same column, move it away first.
- if (gBlankCol == sourceCol) {
- if (gBlankCol == destCol - 1) {
- BlankLeft();
- } else {
- BlankRight();
- }
- }
- // now that they're in different columns, move the blank so it's not below
- MoveBlankToRow(sourceRow);
- }
- // simplify: if the blank is on the same row, and to the left, move up.
- if (gBlankRow == sourceRow && gBlankCol < sourceCol) {
- BlankUp();
- }
- // simplify: if the blank is to the left, move it to the same column.
- if (gBlankCol < sourceCol) {
- MoveBlankToCol(sourceCol);
- }
- // simplify: if the blank is to the upper right, move it to the left and down.
- if (gBlankRow < sourceRow) {
- if (gBlankCol > sourceCol) {
- MoveBlankToCol(sourceCol);
- }
- if (gBlankRow < sourceRow - 1) {
- MoveBlankToRow(sourceRow - 1);
- }
- }
- // if the blank is off to the right, move it next to the source.
- while (gBlankCol > sourceCol + 1) {
- MoveBlankToCol(sourceCol + 1);
- }
- // at this point, the blank should be either just above or just right of the piece.
- if (gBlankCol == sourceCol) {
- if (gBlankRow != sourceRow - 1) Debugger();
- } else {
- if (gBlankRow != sourceRow) Debugger();
- if (gBlankCol != sourceCol + 1) Debugger();
- BlankLeft();
- BlankUp();
- BlankRight();
- }
- BlankDown();
- sourceRow = pieceRow[piece];
- sourceCol = pieceCol[piece];
- if (sourceRow != destRow) goto again1;
- if (gBlankCol != destCol - 1) {
- BlankRight();
- BlankUp();
- BlankLeft();
- while (pieceCol[piece] != destCol) {
- BlankUp();
- BlankRight();
- BlankRight();
- BlankDown();
- BlankLeft();
- }
- return; // DONE!!!!
- }
- BlankLeft();
- BlankUp();
- BlankUp();
- BlankRight();
- BlankRight();
- BlankDown();
- BlankLeft();
- return; // DONE!!!!
- }
-
- // at this point, we know that source is above our destination.
- if (sourceCol >= destCol) { // in this case, we have to move the tile left (or directly down) to get to its destination
- if (sourceCol == destCol) {
-
- // simplify: move the blank so that it is not below the target
- if (gBlankRow > destRow) {
- MoveBlankToRow(destRow);
- }
-
- // move the blank above the source, possibly moving the source
- // down at the same time.
- if (gBlankCol != destCol) {
- if (sourceRow == destRow - 1 && gBlankCol > destCol) {
- MoveBlankToRow(sourceRow - 1);
- MoveBlankToCol(sourceCol - 1);
- BlankDown();
- BlankDown();
- BlankRight();
- BlankUp();
- return; // all done
- } else {
- MoveBlankToRow(sourceRow + 1);
- MoveBlankToCol(sourceCol);
- BlankUp();
- sourceRow++;
- }
- } else {
- if (gBlankRow < sourceRow) {
- MoveBlankToRow(sourceRow - 1);
- } else {
- MoveBlankToRow(sourceRow);
- sourceRow++;
- }
- }
-
- WereInTheSameColumnNow:
- // at this point, the blank is on top of the source,
- // and the puzzle might very well be done already.
- while (sourceRow != destRow) {
- if (nextPiece == piece - nextRow) { // into a column?
- if (gBlankRow != 0 && pieceCol[nextPiece]-pieceRow[nextPiece] >= gBlankCol-gBlankRow) {
- MoveAPiece(nextPiece, gBlankRow, gBlankCol, nextPiece - nextRow);
- }
- if (gBlankCol == destCol) while ((gBlankSquare[-nextRow] == gBlankSquare[nextRow] - 1) && gBlankRow != 0) {
- BlankUp();
- }
- }
- if (gBlankCol == destCol) BlankLeft();
- do {
- BlankDown();
- } while (gBlankRow <= sourceRow);
- BlankRight();
- BlankUp();
- sourceRow++;
- }
- return;
- }
- // simplify: move the blank so that it is to the up of the target
- if (gBlankRow >= destRow) {
- MoveBlankToRow(destRow - 1);
- }
- // simplify: move the blank so that it is not to the left of the target.
- if (gBlankCol < destCol) MoveBlankToCol(destCol);
-
- again2: // simplify: if the blank is to the right of the source, move it so it's not.
- sourceRow = pieceRow[piece];
- sourceCol = pieceCol[piece];
- if (gBlankCol >= sourceCol) {
- // if the blank is in the same row, move it away first.
- if (gBlankRow == sourceRow) {
- if (gBlankRow == destRow - 1) {
- BlankUp();
- } else {
- BlankDown();
- }
- }
- // now that they're in different rows, move the blank so it's not to the right of
- MoveBlankToCol(sourceCol);
- }
- // simplify: if the blank is on the same column, and to the up, move left.
- if (gBlankCol == sourceCol && gBlankRow < sourceRow) {
- BlankLeft();
- }
- // simplify: if the blank is to the up, move it to the same row.
- if (gBlankRow < sourceRow) {
- MoveBlankToRow(sourceRow);
- }
- // simplify: if the blank is to the lower left, move it to the right and up.
- if (gBlankCol < sourceCol) {
- if (gBlankRow > sourceRow) {
- MoveBlankToRow(sourceRow);
- }
- if (gBlankCol < sourceCol - 1) {
- MoveBlankToCol(sourceCol - 1);
- }
- }
- // if the blank is off to the down, move it next to the source.
- while (gBlankRow > sourceRow + 1) {
- MoveBlankToRow(sourceRow + 1);
- }
- // at this point, the blank should be either just below or just to the left of the piece.
- if (gBlankRow == sourceRow) {
- if (gBlankCol != sourceCol - 1) Debugger();
- } else {
- if (gBlankCol != sourceCol) Debugger();
- if (gBlankRow != sourceRow + 1) Debugger();
- BlankUp();
- BlankLeft();
- BlankDown();
- }
- BlankRight();
- sourceRow = pieceRow[piece];
- sourceCol = pieceCol[piece];
- if (sourceCol != destCol) goto again2;
- if (gBlankRow != destRow - 1) {
- BlankDown();
- BlankLeft();
- BlankUp();
- while (pieceRow[piece] != destRow) {
- BlankLeft();
- BlankDown();
- BlankDown();
- BlankRight();
- BlankUp();
- }
- return; // DONE!!!!
- }
- BlankUp();
- BlankLeft();
-
- BlankLeft();
- BlankDown();
- BlankDown();
- BlankRight();
- BlankUp();
- return; // DONE!!!!
- }
-
- // at this point, we know that we are above and to the left of our destination.
-
- if (destCol - sourceCol == destRow - sourceRow) {
- // we're on the diagonal.
- if (gBlankCol >= sourceCol) {
- if (gBlankRow <= sourceRow) goto MoveSrcRightFirst;
- }
- if (gBlankRow >= sourceRow) {
- if (gBlankCol <= sourceCol) goto MoveSrcDownFirst;
- }
- if (gPieceRow[nextPiece] - gPieceCol[nextPiece] > destRow - destCol) {
- // relative to the destination, the next piece is to the lower left.
- // this means we want the blank to end up to the left of the target,
- // rather than above it.
- goto MoveSrcDownFirst;
- }
- goto MoveSrcRightFirst;
- }
- if (destCol - sourceCol < destRow - sourceRow) {
- MoveSrcDownFirst:
- // the source is within the 90-135' octant.
- // we want to move the blank square just below the source.
- // we will end up just to the left of the target.
- if (gBlankCol == sourceCol && gBlankRow < sourceRow) {
- // the blank is on top of the source. moving it
- // down would move our square in the wrong direction.
- BlankRight();
- }
- // in case the source is just to the upper left of the target,
- // we have to make sure we don't accidentally munge the
- // protected area.
- if (gBlankCol > destCol) MoveBlankToCol(destCol);
- MoveBlankToRow(sourceRow + 1);
- MoveBlankToCol(sourceCol);
- BlankUp();
- sourceRow++;
- BlankRight();
- BlankDown();
- // the blank is now to the right of the source.
- } else {
- MoveSrcRightFirst:
- // the source is within the 135-180' octant.
- // we want to move the blank square just to the right of the source.
- // we will end up just above the target.
- if (gBlankRow == sourceRow && gBlankCol < sourceCol) {
- // the blank is to the left of the source. moving it
- // to the right would move our square in the wrong direction.
- BlankDown();
- }
- // in case the source is just to the upper left of the target,
- // we have to make sure we don't accidentally munge the
- // protected area.
- if (gBlankRow > destRow) MoveBlankToRow(destRow);
- MoveBlankToCol(sourceCol + 1);
- MoveBlankToRow(sourceRow);
- // the blank is now to the right of the source.
- }
- BlankLeft();
- sourceCol++;
- // the blank is now to the left of the source.
- // are we done yet?
- for (;;) {
- if (sourceCol == destCol) {
- if (sourceRow == destRow) return;
- // the blank is still to the left of the source.
- BlankDown();
- BlankRight();
- BlankUp();
- sourceRow++;
- goto WereInTheSameColumnNow;
- }
- if (sourceRow == destRow) {
- goto WereOnTheSameRowNow;
- }
- BlankDown();
- BlankRight();
- BlankUp();
- sourceRow++;
- BlankRight();
- BlankDown();
- BlankLeft();
- sourceCol++;
- }
- }
-
- static void *QuickBlock(long size) {
- Handle h = NewHandle(size);
-
- if (h == 0) return 0;
- HLock(h);
- return *h;
- }
-
- static void DisposeBlock(void *block) {
- Handle h = RecoverHandle(block);
-
- DisposeHandle(h);
- }
-
- void SolveTiles(
- long *tiles, /* pointer to array of tiles where */
- long numRows, /* tile (row,col) is at */
- long numCols, /* *(tiles + row*numCols + col) */
- MoveProc MakeMove /* Callback procedure to move a tile */
- ) {
- long col, row, target, tile, correctTile;
- long colsToGo, rowsToGo;
- long *tileRover;
- long *pieceRow, *pieceCol;
-
- pieceRow = QuickBlock(numRows * numCols * sizeof(long));
- if (pieceRow == 0) return;
-
- pieceCol = QuickBlock(numRows * numCols * sizeof(long));
- if (pieceCol == 0) {
- DisposeBlock(pieceRow);
- return;
- }
- gPieceRow = pieceRow;
- gPieceCol = pieceCol;
-
- gMakeMove = MakeMove;
- gTiles = tiles;
- gNumCols = numCols;
-
- tileRover = tiles;
- correctTile = 0;
- for (row = 0; row < numRows; row++) {
- for (col = 0; col < numCols; col++) {
- tile = *tileRover++;
- pieceRow[tile] = row;
- pieceCol[tile] = col;
- correctTile++;
- }
- }
-
- gBlankSquare = &tiles[gBlankRow * numCols + gBlankCol];
-
- rowsToGo = numRows;
- colsToGo = numCols;
-
- for (;;) {
- if (rowsToGo >= colsToGo) {
- if (rowsToGo <= 2) break;
- row = rowsToGo - 1;
- for (col = colsToGo - 1; col > 1; col--) {
- tile = row * numCols + col;
- MoveAPiece(tile, row, col, tile - 1);
- }
- tile = row * numCols;
- if (pieceRow[tile] != row || pieceCol[tile ] != 0 ||
- pieceRow[tile + 1] != row || pieceCol[tile + 1] != 1) {
- MoveAPiece(tile, row, 1, tile + 1);
- if (gBlankRow == row) {
- if (tiles[(row - 1) * numCols] == tile + 1) {
- // problem scenario 1
- rowScenario1:
- BlankRight();
- BlankUp();
- BlankLeft();
- BlankUp();
- BlankRight();
- BlankDown();
- BlankDown();
- BlankLeft();
- BlankUp();
- BlankRight();
- BlankUp();
- BlankLeft();
- BlankDown();
- BlankDown();
- BlankRight();
- BlankUp();
- goto nextRow;
- }
- } else if (tiles[row * numCols] == tile + 1) {
- // problem scenario 2
- MoveBlankToCol(0);
- MoveBlankToRow(row);
- goto rowScenario1;
- }
- MoveAPiece(tile + 1, row, 1, 0);
- }
- nextRow: if (gBlankRow == row) BlankUp();
- if (pieceRow[tile] != row || pieceCol[tile ] != 0) {
- // the "12" isn't in place...
- Debugger();
- Debugger();
- Debugger();
- Debugger();
- }
- rowsToGo--;
- } else {
- if (colsToGo <= 2) break;
- col = colsToGo - 1;
- for (row = rowsToGo - 1; row > 1; row--) {
- tile = row * numCols + col;
- MoveAPiece(tile, row, col, tile - numCols);
- }
- if (pieceRow[ col] != 0 || pieceCol[ col] != col ||
- pieceRow[numCols + col] != 1 || pieceCol[numCols + col] != col) {
- MoveAPiece(col, 1, col, col + numCols);
- if (gBlankCol == col) {
- if (tiles[col - 1] == numCols + col) {
- // problem scenario 1
- colScenario1:
- BlankDown();
- BlankLeft();
- BlankUp();
- BlankLeft();
- BlankDown();
- BlankRight();
- BlankRight();
- BlankUp();
- BlankLeft();
- BlankDown();
- BlankLeft();
- BlankUp();
- BlankRight();
- BlankRight();
- BlankDown();
- BlankLeft();
- goto nextCol;
- }
- } else if (tiles[col] == numCols + col) {
- // problem scenario 2
- MoveBlankToRow(0);
- MoveBlankToCol(col);
- goto colScenario1;
- }
- MoveAPiece(numCols + col, 1, col, 0);
- }
- nextCol: if (gBlankCol == col) BlankLeft();
- if (pieceRow[ col] != 0 || pieceCol[ col] != col) {
- // the "3" isn't in place...
- Debugger();
- Debugger();
- Debugger();
- Debugger();
- }
- colsToGo--;
- }
- }
-
- if (gBlankRow == 0) {
- if (gBlankCol == 0) {
- if (pieceRow[1] == 0) goto pos0123;
- if (pieceCol[1] == 0) goto pos0312;
- goto pos0231;
- } else {
- if (pieceRow[1] == 0) goto pos1023;
- if (pieceCol[1] == 0) goto pos3012;
- goto pos2031;
- }
- } else {
- if (gBlankCol == 0) {
- if (pieceRow[1] != 0) goto pos3201;
- if (pieceCol[1] == 0) goto pos1302;
- goto pos2103;
- } else {
- if (pieceRow[1] != 0) goto pos3210;
- if (pieceCol[1] == 0) goto pos1320;
- goto pos2130;
- }
- }
-
- // 30
- // 12
- pos3012:
- BlankLeft();
-
- // 03
- // 12
- pos0312:
- BlankDown();
-
- // 13
- // 02
- pos1302:
- BlankRight();
-
- // 13
- // 20
- pos1320:
- BlankUp();
-
- // 10
- // 23
- pos1023:
- BlankLeft();
-
- // 01
- // 23
- goto all_done;
-
- // 32
- // 10
- pos3210:
- BlankLeft();
-
- // 32
- // 01
- pos3201:
- BlankUp();
-
- // 02
- // 31
- pos0231:
- BlankRight();
-
- // 20
- // 31
- pos2031:
- BlankDown();
-
- // 21
- // 30
- pos2130:
- BlankLeft();
-
- // 21
- // 03
- pos2103:
- BlankUp();
-
- // 01
- // 23
- pos0123:
- goto all_done;
-
- all_done:
- DisposeBlock(pieceRow); gPieceRow = 0;
- DisposeBlock(pieceCol); gPieceCol = 0;
- }
-